﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Arcadium.GameEntities;
using Arcadium.Graphics;
using Arcadium.GameEntities.Projectiles;
using Arcadium.Audio;
using Arcadium.GameEntities.Characters;
using Microsoft.Xna.Framework.Net;

namespace Arcadium
{
    abstract class World
    {
        #region Field

        protected Random random;

        protected List<GameEntity> entities;
        
        protected Map map;

        protected Dictionary<long, Character> enemiesD;
        protected Dictionary<long, Projectile> projectilesD;

        protected List<Character> enemies;
        protected List<Projectile> projectiles;

        protected List<Crate> crates;

        #endregion

        #region Properties

        public Random Random
        {
            get { return random; }
        }

        // Entities

        public Map Map
        {
            get { return map; }
        }

        public Dictionary<long, Character> EnemiesD { get { return enemiesD; } }
        public Dictionary<long, Projectile> ProjectilesD { get { return projectilesD; } }

        public List<Character> Enemies { get { return enemies; } }
        public List<Projectile> Projectiles { get { return projectiles; } }
        
        public List<Crate> Crates { get { return crates; } }

        // Changes

        public WorldChanges Changes { get; set; }

        #endregion

        #region Initialization

        /// <summary>
        /// Constructs a new World.
        /// </summary>
        public World()
        {
            random = new Random();

            entities = new List<GameEntity>();

            map = new Map(this);

            enemiesD = new Dictionary<long,Character>();
            projectilesD = new Dictionary<long,Projectile>();

            enemies = new List<Character>();
            projectiles = new List<Projectile>();

            crates = new List<Crate>();

            Changes = new WorldChanges();
        }

        public virtual void LoadContent(ContentManager content, GameSessionProperties sessionProperties)
        {
            if (sessionProperties.Map == GameSessionProperties.MapType.ConstructionYard)
            {
                map.SetMask(content.Load<Texture2D>("Gameplay\\Maps\\Lvl1\\lvl1_mask"));
                map.AddBackground(content.Load<Texture2D>("Gameplay\\Maps\\Lvl1\\lvl1_background01"));
                map.AddSpawnArea(new Vector2(480, 337), Vector2.UnitX * 959);
                map.AddSpawnArea(new Vector2(79, 540), Vector2.UnitX * 400);
                map.AddSpawnArea(new Vector2(1440, 540), Vector2.UnitX * 398);
                map.AddSpawnArea(new Vector2(480, 742), Vector2.UnitX * 959);
                map.AddSpawnArea(new Vector2(79, 945), Vector2.UnitX * 400);
                map.AddSpawnArea(new Vector2(1440, 945), Vector2.UnitX * 398);
                map.AddSpawnArea(new Vector2(480, 1012), Vector2.UnitX * 398);
                map.AddSpawnArea(new Vector2(1039, 1012), Vector2.UnitX * 400);
                map.AddPOI(
                    new Vector2(960, 742), Vector2.Zero, Vector2.Zero, Vector2.Zero, // Spawn 1-4
                    new Vector2(960, 25)); // EnemyDefaultRespawn
            }
            else if (sessionProperties.Map == GameSessionProperties.MapType.RocketSilo)
            {

            }
            AudioManager.PlaySong(2 + (int)sessionProperties.Map);

            // Load all static textures etc.
            CharacterTextures.LoadContent(content);
            ProjectileTextures.LoadContent(content);

            HandGun.LoadContent(content);
            Crate.LoadContent(content);

            AddEntity(map);
        }

        #endregion

        #region Update and Draw

        /// <summary>
        /// Updates the world.
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void Update(GameTime gameTime)
        {
            for (int i = 0; i < entities.Count; i++)
            {
                if (entities[i].Update(gameTime))
                    RemoveEntity(entities[i--]);
            }
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            foreach (GameEntity entity in entities)
                entity.Draw(spriteBatch);
        }

        #endregion

        #region Public Methods

        // Add
        public void AddEntity(GameEntity entity)
        {
            this.entities.Add(entity);
            if (entity is Character)
                Add((entity as Character));
            else if (entity is Projectile)
                Add((entity as Projectile));
            else if (entity is Crate)
                Add((entity as Crate));
        }

        protected virtual void Add(Character enemy)
        {
            enemiesD.Add(enemy.Id, enemy);
            enemies.Add(enemy);
            Changes.EnemyAdded = enemy;
        }
        protected virtual void Add(Projectile projectile)
        {
            projectilesD.Add(projectile.Id, projectile);
            projectiles.Add(projectile);
            Changes.ProjectilesAdded.Add(projectile);
        }
        protected virtual void Add(Crate crate)
        {
            crates.Add(crate);
        }

        // Remove
        public void RemoveEntity(GameEntity entity)
        {
            this.entities.Remove(entity);
            if (entity is Character)
                Remove((entity as Character));
            else if (entity is Projectile)
                Remove((entity as Projectile));
            else if (entity is Crate)
                Remove((entity as Crate));
        }

        protected virtual void Remove(Character enemy)
        {
            enemiesD.Remove(enemy.Id);
            enemies.Remove(enemy);
            Changes.EnemyRemoved = enemy;
        }
        protected virtual void Remove(Projectile projectile)
        {
            projectilesD.Remove(projectile.Id);
            projectiles.Remove(projectile);
            Changes.ProjectilesRemoved.Add(projectile);
        }
        protected virtual void Remove(Crate crate)
        {
            crates.Remove(crate);
        }

        #endregion
    }
}
